import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * Stubs generator which can create a stub java file and compile it just in time.
 */
public class StubsManager {
	
	private final static boolean cleanOnExit = true; // If set, all stub related files will be deleted when the JVM ends.
	
	// Crate a stub java file from a simple name
	public static void generateStub(String name) throws IOException {
		File f = new File(name+"_stub.java");
		if(!f.exists()) {
			FileWriter fw = null;
			String content;
			try {
				Class<?> itf = Class.forName(name+"_itf");
				fw = new FileWriter(f);
				if (!itf.isInterface()) {
					System.err.println("The class must be an interface");
					System.exit(1);
				}
				
				Method[] methodes = itf.getDeclaredMethods();
				
				content = "public class "+name+"_stub extends SharedObject implements "+name+"_itf, java.io.Serializable {\n\n"
						+"\tprivate static final long serialVersionUID = 1L;\n\n";
				
				for (int i = 0; i < methodes.length; i++) {
		            Method meth = methodes[i];
		            Class<?>[] params = meth.getParameterTypes();
		            if (Modifier.isPublic(meth.getModifiers())) content += "\tpublic ";
		            else if (Modifier.isPrivate(meth.getModifiers())) content += "\tprivate ";
		            else if (Modifier.isProtected(meth.getModifiers())) content += "\tprotected ";
		            content += meth.getReturnType().getName()+" ";
		            content += meth.getName()+"(";
		            for (int j = 0; j < params.length; j++) {
		                content += " "+params[j].getName()+" obj"+j+",";
		            }
		            if (params.length>0) content = content.substring(0, content.length()-1);
		            content += ") {\n";
		            content += "\t\t"+name+" o = ("+name+")obj;\n";
		            content += "\t\t";
		            if (!meth.getReturnType().getName().equals("void")) content += "return ";
		            content += "o."+meth.getName()+"(";
		            for (int j = 0; j < params.length; j++) {
		                content += "obj"+j+",";
		            }
		            if (params.length>0) content = content.substring(0, content.length()-1);
		            content += ");\n\t}\n";
		        }
				content += "}\n\n";

				fw.write(content);
				
			} catch (Exception e) {
				System.out.println("Generation of "+name+"_stub.java failed");
				e.printStackTrace();
				System.exit(1);
			} finally {
				if(fw != null)
					fw.close();
			}
		}
		
		if(cleanOnExit) new File(name+"_stub.java").deleteOnExit();
	}
	
	// Compile a java file into a bytecode file
	public static void compileStub(String name) {
		String cmd = "javac "+name+"_stub.java";
		try {
			String s;
			Process p = Runtime.getRuntime().exec(cmd);
			
			 int i = p.waitFor();
             if (i == 0){
                     // STDOUT
                     BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
                     while ((s = input.readLine()) != null)
                             System.out.println(s);
             } else {
                     // STDERR
                     BufferedReader stderr = new
                     BufferedReader(new InputStreamReader(p.getErrorStream()));
                     while ((s = stderr.readLine()) != null)
                             System.out.println(s);
                     throw new Exception();
             }
             
             if(cleanOnExit) new File(name+"_stub.class").deleteOnExit();

			
		} catch (Exception e) {
			System.out.println("Compilation of "+name+"_sutb.java failed");
			e.printStackTrace();
			System.exit(1);
		}
	}
}